import { isArray, isNil, find, remove, forEach } from 'lodash'

// const errorLog = console.error ? console.error : console.log

const execFunc = (cbs, ...args) => {
  forEach(cbs, cb => {
    try {
      cb && cb(...args)
    } catch (e) {
      // errorLog(e)
    }
  })
}

class EventBus {
  constructor () {
    // [{
    //   label: '下拉框切换事件',
    //   type: 'selectChange',
    //   evt: [{
    //     name: Symbol('selectChange'),
    //     cb: function () {}
    //   },  ...]
    // }, {
    //   label: '',
    //   name: '',
    //   cb:
    // }, ...]
    this.events = []

    this.actions = []
  }

  /**
   * 注册一个事件，先指定其类型，再申明其事件名称和回调函数
   * 用法如下：
   *  this.globalEvent.type('globalInitTime')('initTime', function () {})
   *  this.globalEvent.type('globalInitTime', '全局时间初始化事件')('initTime', function () {})
   * @param {*} typeArgs(type, label) 类型参数
   */
  type (...typeArgs) {
    return (...args) => {
      const [name, cb, type, label] = [...args, ...typeArgs]
      return this.on(name, cb, type, label)
    }
  }

  /**
   * 修改指定类型/名称的事件对象的label
   * @param {*} typeArgs(type, label) 参数
   */
  label (...typeArgs) {
    return (...args) => {
      const [type, label] = [...args, ...typeArgs]
      const event = this.events.find(e => e.type === type || e.name === type)
      event && label && (event.label = label)
    }
  }

  /**
   * `registerEvents`的别名
   * @param {*} eventArgs(name, cb, type, label) 事件对象参数
   * @param {function} cb 回调函数
   */
  on (name, cb, type, label) {
    this.registerEvents({ name, cb, type, label })
    return this
  }

  /**
   * `triggerEvents`的别名
   * 事件派发，通过指定事件类型/名称，
   * 找到第一个匹配的事件，触发其方法或其内部所有事件的方法
   * @param {string|symbol} type 事件类型/名称
   * @param {...*} parameter 参数
   */
  emit (type, ...args) {
    this.triggerEvents(type, ...args)
    return this
  }

  /**
   * `unRegisterEvents`的别名
   * 事件解除注册，指定事件名/类型
   * @param {string|Array<string>} types 事件名或者类型
   */
  off (types) {
    this.unRegisterEvents(types)
    return this
  }

  /**
   * 事件注册，只有注册的事件才能被分发
   * @param {Object|Array<Object>} event eg：{
   *  label: '下拉框切换事件',
   *  type: 'selectChange',  // 可选字段
   *  name: Symbol('selectChange'),
   *  cb: function () {}
   * }
   */
  registerEvents (event) {
    event = isArray(event) ? event : [event]
    event.forEach(e => {
      const { label = '-', type, name, cb } = e
      if (!isNil(type)) {
        const event = find(this.events, it => it.type === type)
        if (!event) {
          this.events.push({
            label,
            type,
            evt: [{ name, cb: [cb] }]
          })
        } else {
          const evt = find(event.evt, e => e.name === name)
          evt ? evt.cb.push(cb) : event.evt.push({ name, cb: [cb] })
        }
      } else {
        const event = find(this.events, it => it.name === name)
        event
          ? event.cb.push(cb)
          : this.events.push({
            label,
            name,
            cb: [cb]
          })
      }
    })
    return this
  }

  /**
   * 事件派发，通过指定事件类型/名称，
   * 找到第一个匹配的事件，触发其方法或其内部所有事件的方法
   * @param {string|symbol} type 事件类型/名称
   * @param {...*} parameter 参数
   */
  triggerEvents (type) {
    const args = [].slice.call(arguments, 1)
    const event = find(this.events, e => e.type === type || e.name === type)
    if (event) {
      if (event.evt) {
        forEach(event.evt, e => {
          e.cb && execFunc(e.cb, ...args)
        })
      } else {
        event.cb && execFunc(event.cb, ...args)
      }
    }
    return this
  }

  /**
   * 事件解除注册，指定事件名/类型
   * @param {string|Array<string>} types 事件名或者类型
   */
  unRegisterEvents (types) {
    if (isNil(types)) {
      this.events = []
      return
    }
    types = isArray(types) ? types : [types]
    forEach(types, type => {
      remove(this.events, event => event.type === type || event.name === type)
      forEach(this.events, event => {
        event.evt && remove(event.evt, evt => evt.name === type)
      })
    })
    // console.log(`events is `, this.events)
    return this
  }
}

const eventBus = new EventBus()

export default eventBus
